package net.pimpas.web.jsf.components.ajax;

import java.io.IOException;

import javax.faces.FactoryFinder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.pimpas.logging.serviceprovider.LoggingServiceProvider;
import net.pimpas.logging.serviceprovider.LoggingServiceProviderFactoryImpl;
import net.pimpas.web.jsf.components.ajax.context.ResponseWriterAjaxWrapper;
import net.pimpas.web.jsf.renderers.AjaxRendererBase;
import net.pimpas.web.jsf.util.AjaxUtils;
import net.pimpas.web.jsf.util.ComponentUtils;

public class AjaxPhaseRendererListener implements PhaseListener {

	private static final long serialVersionUID = 1505068949676180602L;
	private static final LoggingServiceProvider logger = 
		LoggingServiceProviderFactoryImpl.getLoggingServiceProvider(AjaxPhaseRendererListener.class);

	public void afterPhase(PhaseEvent event) {
		
	}

	public void beforePhase(PhaseEvent event) {
		
		// ajax request?!
		FacesContext context = event.getFacesContext();
		HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
		HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
		if(request.getParameter(AjaxSupport.AJAX_SUPPORT) == null)
			return;
		
		String caller = (String) request.getParameter(AjaxSupport.AJAX_CALLER);
		if(caller == null) return;
		UIComponent component = ComponentUtils.getComponentById(context.getViewRoot(), context, caller, ComponentUtils.CLIENT_ID);
		if(component == null) return;
		// have ajax mode?!
		// ajax mode enabled
		logger.info("[AJAXPHASERENDERER]: AjaxRequest Found... Processing Caller!");
		logger.info("[AJAXPHASERENDERER]: Caller Found... Component with SERVER ID: ["+component.getId()+"]");
		String ajaxObject = request.getParameter(UIAjax.AJAX_OBJECT_REQUEST_KEY);
		UIAjax ajax = (UIAjax) ComponentUtils.getComponentById(component, context, ajaxObject, ComponentUtils.CLIENT_ID);
		if(ajax == null) {
			logger.error("[AJAXPHASERENDERER]: Something weird... component with SERVER ID ["+component.getId()+"]" +
					" doenst have a component of type UIAjaxPopulate in children list.]");
			logger.error("[AJAXPHASERENDERER]: Cancelling AJAX PHASE RENDER...");
			return;
		}
		
		// get ajax render kit
		RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
		RenderKit ajaxKit = factory.getRenderKit(context, "HTML_AJAX");
		logger.info("[AJAXPHASERENDERER]: AjaxRenderKit its ok... processing renderers!");
		// hack faces
		ResponseWriterAjaxWrapper wrapper = new ResponseWriterAjaxWrapper(context.getResponseWriter());
		context.setResponseWriter(wrapper);
		logger.info("[AJAXPHASERENDERER]: ResponseWriterAjaxWrapper initialized...");
		
		String[] renders = ajax.getRender().split(",");
		UIComponent cp = null;
		AjaxRendererBase renderer = null;
		try {
			wrapper.startDocument();
		} catch (IOException e1) {
		}
		
		String r = null;
		for(int i=0;i<renders.length;i++) {
			r = renders[i].trim();
			cp = ComponentUtils.getComponentById(context.getViewRoot(), context, r, ComponentUtils.SERVER_ID);
			if(cp == null) {
				logger.error("[AJAXPHASERENDERER]: Cannot find component with ID ["+r+"] to Render. Cotinue with next.");
				continue;
			}
			logger.info("[AJAXPHASERENDERER]: Getting AJAX Render for component ["+r+"]");
			// get renderer
			renderer = (AjaxRendererBase) ajaxKit.getRenderer(cp.getFamily(), cp.getRendererType());
			if(renderer == null) {
				logger.warn("[AJAXPHASERENDERER]: Cannot find AJAX RENDER for component ["+cp.getId()+"]." +
						" Family: "+cp.getFamily()+", RendererType: "+cp.getRendererType());
				logger.info("[AJAXPHASERENDERER]: Using Default AJAX RENDER for component ["+cp.getId()+"]");
				renderer = (AjaxRendererBase) ajaxKit.getRenderer(AjaxUtils.DEFAULT_COMPONENT_FAMILY, AjaxUtils.DEFAULT_RENDERER_TYPE);
			}
			logger.info("[AJAXPHASERENDERER]: AjaxRenderer found... starting render.");
			wrapper.startForComponent(context, cp, renderer);
			// render
			try {
				logger.info("[AJAXPHASERENDERER]: Starting encode using renderer ["+renderer.getClass().getName()+"]");
				renderer.encodeBegin(context, cp);
				renderer.encodeChildren(context, cp);
				renderer.encodeEnd(context, cp);
				logger.info("[AJAXPHASERENDERER]: Finished encode using renderer ["+renderer.getClass().getName()+"]");
			} catch(IOException e) {
				logger.fatal("[AJAXPHASERENDERER]: Fatal error in listener... see the full log!", e);
				throw new RuntimeException(e);
			}
		}
		
		try {
			wrapper.endDocument();
		} catch (IOException e1) {
			
		}
		
		AjaxUtils.ensureCorrectClient(context.getExternalContext());
		
		try {
			response.getWriter().write(wrapper.getResponseText());
			response.getWriter().flush();
			response.getWriter().close();
		} catch (IOException e) {
			logger.fatal("[AJAXPHASERENDERER]: Fatal error in listener... see the full log!", e);
			throw new RuntimeException(e);
		}
		
		// finish response
		context.responseComplete();
		// save view
		AjaxUtils.saveView(context);
		
		logger.info("[AJAXPHASERENDERER]: Finished Response! Exiting Listener...");
	}

	public PhaseId getPhaseId() {
		return PhaseId.RENDER_RESPONSE;
	}
	
}